/**
 * Copyright (c) 2009 Koder Chen. All Rights Reserved.
 *
 * tst_bigint.cpp
 *
 * The file implements the functions for the test of big integer manipulation
 *
 *  Author: chenfuhan
 * Created: 2009-6-23
 * Version: 1.0
 */
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

#include "tst_helper.h"
#include "tst_bigint.h"

void test_add()
{
	printf("Test add...........");
	fflush(stdout);

	srand(time(NULL));
	u8_pt a1 = rsa_default_malloc();
	u8_pt a2 = rsa_default_malloc();
	u32_t t1 = 0, t2 = 0, t3 = 0;
	i32_t i = 0, j = 0, k=0, l=0;

	for(i = 0; i < 100; i++)
	{
		t1 = rand() % 32767;
		bi_reset(a1);
		int2bytes(t1, a1);
		for(j = 0; j < 100;j++)
		{
			t2 = rand() % 32767;
			bi_reset(a2);
			int2bytes(t2, a2);
			bi_add(a1, a2, a2);
			t3 = bytes2int(a2);
			if(t3 != t1+t2)
			{
				printf("%d+%d=%d, %d\n", t1, t2, t1+t2, t3);
				l = bi_bits(a2);
				for(k=l-1;k>=0;k--)
				{
					printf("%d", bi_get_bit(a2, k));
				}
				printf("\n");
				exit(0);
			}
		}
	}
	rsa_free(a1);
	rsa_free(a2);
	printf("Ok\n");
	fflush(stdout);
}

void test_sub()
{
	printf("Test sub...........");
	fflush(stdout);

	srand(time(NULL));
	u8_pt a1 = rsa_default_malloc();
	u8_pt a2 = rsa_default_malloc();
	u32_t t1 = 0, t2 = 0, t3 = 0;
	i32_t i = 0, j = 0, l = 0, k = 0;

	for(i = 0; i < 100; i++)
	{
		t1 = rand() % 32767;
		bi_reset(a1);
		int2bytes(t1, a1);
		for(j = 0; j < 100;j++)
		{
			t2 = rand() % 32767;
			bi_reset(a2);
			int2bytes(t2, a2);
			if(t2 > t1)
				bi_sub(a2, a1, a2);
			else
				bi_sub(a1, a2, a2);
			t3 = bytes2int(a2);
			if(t3 != t1-t2 && t3 != t2 - t1)
			{
				printf("abs(%d-%d)=%d, %d\n", t1, t2, abs(t1-t2), t3);
				l = bi_bits(a2);
				for(k=l-1;k>=0;k--)
				{
					printf("%d", bi_get_bit(a2, k));
				}
				printf("\n");
				exit(0);
			}
		}
	}
	rsa_free(a1);
	rsa_free(a2);
	printf("Ok\n");
	fflush(stdout);
}

void test_mul()
{
	printf("Test mul...........");
	fflush(stdout);

	srand(time(NULL));
	u8_pt a1 = rsa_default_malloc();
	u8_pt a2 = rsa_default_malloc();
	u32_t t1 = 0, t2 = 0, t3 = 0;
	i32_t i = 0, j = 0;

	for(i = 0; i < 100; i++)
	{
		t1 = rand() % 32767;
		bi_reset(a1);
		int2bytes(t1, a1);
		for(j = 0; j < 100;j++)
		{
			t2 = rand() % 32767;
			bi_reset(a2);
			int2bytes(t2, a2);
			bi_mul(a1, a2, a2);
			t3 = bytes2int(a2);
			assert(t3 == t1 * t2);
		}
	}
	rsa_free(a1);
	rsa_free(a2);
	printf("Ok\n");
	fflush(stdout);
}

void test_div()
{
	printf("Test div...........");
	fflush(stdout);

	srand(time(NULL));
	u8_pt a1 = rsa_default_malloc();
	u8_pt a2 = rsa_default_malloc();
	u8_pt a3 = rsa_default_malloc();
	u8_pt a4 = rsa_default_malloc();
	u32_t t1 = 0, t2 = 0, t3 = 0, t4 = 0;
	i32_t i = 0, j = 0;

	for(i = 0; i < 100; i++)
	{
		t1 = rand() % 32767;
		bi_reset(a1);
		int2bytes(t1, a1);
		for(j = 0; j < 100;j++)
		{
			t2 = rand() % 32767 + 1;
			bi_reset(a2);
			int2bytes(t2, a2);
			bi_div(a1, a2, a3, a4);
			t3 = bytes2int(a3);
			t4 = bytes2int(a4);
			assert(t3 == t1 / t2);
			assert(t4 == t1 % t2);
		}
	}
	rsa_free(a1);
	rsa_free(a2);
	rsa_free(a3);
	rsa_free(a4);
	printf("Ok\n");
	fflush(stdout);
}

void test_mod()
{
	printf("Test mod...........");
	fflush(stdout);

	srand(time(NULL));
	u8_pt a1 = rsa_default_malloc();
	u8_pt a2 = rsa_default_malloc();
	u8_pt a3 = rsa_default_malloc();
	u32_t t1 = 0, t2 = 0, t3 = 0;
	i32_t i = 0, j = 0;

	for(i = 0; i < 100; i++)
	{
		t1 = rand() % 32767;
		bi_reset(a1);
		int2bytes(t1, a1);
		for(j = 0; j < 100;j++)
		{
			t2 = rand() % 32767 + 1;
			bi_reset(a2);
			int2bytes(t2, a2);
			bi_mod(a1, a2, a3);
			t3 = bytes2int(a3);
			assert(t3 == t1 % t2);
		}
	}
	rsa_free(a1);
	rsa_free(a2);
	rsa_free(a3);
	printf("Ok\n");
	fflush(stdout);
}

void test_lshift()
{
	printf("Test lshift...........");
	fflush(stdout);

	srand(time(NULL));
	u8_pt a1 = rsa_default_malloc();
	u32_t t1 = 0, t2 = 0, t3 = 0;
	i32_t i = 0;

	for(i = 0; i < 10000; i++)
	{
		t1 = rand() % 32767;
		bi_reset(a1);
		int2bytes(t1, a1);
		t2 = rand() % 32;
		bi_lshift(a1, t2);
		t3 = bytes2int(a1);
		assert((t1 << t2) == t3);
	}
	rsa_free(a1);
	printf("Ok\n");
	fflush(stdout);
}

void test_rshift()
{
	printf("Test rshift...........");
	fflush(stdout);

	srand(time(NULL));
	u8_pt a1 = rsa_default_malloc();
	u32_t t1 = 0, t2 = 0, t3 = 0;
	i32_t i = 0;

	for(i = 0; i < 10000; i++)
	{
		t1 = rand() % 32767;
		bi_reset(a1);
		int2bytes(t1, a1);
		t2 = rand() % 32;
		bi_rshift(a1, t2);
		t3 = bytes2int(a1);
		assert((t1 >> t2) == t3);
	}
	rsa_free(a1);
	printf("Ok\n");
	fflush(stdout);
}

void test_reset()
{
	printf("Test reset...........");
	fflush(stdout);

	srand(time(NULL));
	u8_pt a1 = rsa_default_malloc();
	u32_t t1 = 0, t2 = 0;
	i32_t i = 0;

	for(i = 0; i < 10000; i++)
	{
		t1 = rand() % 32767;
		bi_reset(a1);
		int2bytes(t1, a1);
		bi_reset(a1);
		t2 = bytes2int(a1);
		assert(0 == t2);
	}
	rsa_free(a1);
	printf("Ok\n");
	fflush(stdout);
}

void test_bits()
{
	printf("Test bits...........");
	fflush(stdout);

	srand(time(NULL));
	u8_pt a1 = rsa_default_malloc();
	u32_t t1 = 0, t2 = 0, t3 = 0;
	i32_t i = 0;

	for(i = 0; i < 10000; i++)
	{
		t1 = rand() % 32767;
		bi_reset(a1);
		int2bytes(t1, a1);
		t2 = bi_bits(a1);
		t3 = intbits(t1);
		assert(t2 == t3);
	}
	rsa_free(a1);
	printf("Ok\n");
	fflush(stdout);
}

void test_comp()
{
	printf("Test comp...........");
	fflush(stdout);

	srand(time(NULL));
	u8_pt a1 = rsa_default_malloc();
	u8_pt a2 = rsa_default_malloc();
	u32_t t1 = 0, t2 = 0, t3 = 0;
	i32_t i = 0, j = 0;

	for(i = 0; i < 100; i++)
	{
		t1 = rand() % 32767;
		bi_reset(a1);
		int2bytes(t1, a1);
		for(j = 0; j < 100;j++)
		{
			t2 = rand() % 32767;
			bi_reset(a2);
			int2bytes(t2, a2);
			t3 = bi_comp(a1, a2);
			if(t1 == t2) assert(t3 == 0);
			else if(t1 > t2) assert(t3 == 1);
			else assert(t3 == -1);
		}
	}
	rsa_free(a1);
	rsa_free(a2);
	printf("Ok\n");
	fflush(stdout);
}

void test_set_bit()
{
	printf("Test set bit...........");
	fflush(stdout);

	srand(time(NULL));
	u8_pt a1 = rsa_default_malloc();
	u32_t t1 = 0, t2 = 0, t3 = 0;
	i32_t i = 0;

	for(i = 0; i < 10000; i++)
	{
		bi_reset(a1);
		t1 = rand() % 32;
		bi_set_bit(a1, t1, 1);
		t2 = bytes2int(a1);
		t3 = intbits(t2);
		assert(t3 == t1 + 1);
	}
	rsa_free(a1);
	printf("Ok\n");
	fflush(stdout);
}

void test_get_bit()
{
	printf("Test get bit...........");
	fflush(stdout);

	srand(time(NULL));
	u8_pt a1 = rsa_default_malloc();
	u32_t t1 = 0;
	i32_t i = 0;

	for(i = 0; i < 10000; i++)
	{
		bi_reset(a1);
		t1 = rand() % 32;
		bi_set_bit(a1, t1, 1);
		t1 = bi_get_bit(a1, t1);
		assert(1 == t1);
		bi_set_bit(a1, t1, 0);
		t1 = bi_get_bit(a1, t1);
		assert(0 == t1);
	}
	rsa_free(a1);
	printf("Ok\n");
	fflush(stdout);
}

void test_gcd()
{
	printf("Test gcd...........");
	fflush(stdout);

	srand(time(NULL));
	u8_pt a1 = rsa_default_malloc();
	u8_pt a2 = rsa_default_malloc();
	u8_pt a3 = rsa_default_malloc();
	u32_t t1 = 0, t2 = 0, t3 = 0;
	i32_t i = 0, j = 0;

	for(i = 0; i < 100; i++)
	{
		t1 = rand() % 32767;
		bi_reset(a1);
		int2bytes(t1, a1);
		for(j = 0; j < 100;j++)
		{
			t2 = rand() % 32767;
			bi_reset(a2);
			bi_reset(a3);
			int2bytes(t2, a2);
			bi_gcd(a1, a2, a3);
			t3 = bytes2int(a3);
			//printf("gcd(%d, %d) = %d, %d)\n", t1, t2, t3, gcd(t1, t2));
			assert(t3 == gcd(t1, t2));
		}
	}
	rsa_free(a1);
	rsa_free(a2);
	rsa_free(a3);
	printf("Ok\n");
	fflush(stdout);
}

void test_powm()
{
	printf("Test powm...........");
	fflush(stdout);

	srand(time(NULL));
	u8_pt a1 = rsa_default_malloc();
	u8_pt a2 = rsa_default_malloc();
	u8_pt a3 = rsa_default_malloc();
	u8_pt a4 = rsa_default_malloc();
	u32_t t1 = 0, t2 = 0, t3 = 0, t4 = 0;
	i32_t i = 0, j = 0, k = 0;

	for(i = 0; i < 50; i++)
	{
		t1 = rand() % 32767;
		bi_reset(a1);
		int2bytes(t1, a1);
		for(j = 0; j < 10; j++)
		{
			t2 = rand() % 32767;
			bi_reset(a2);
			int2bytes(t2, a2);
			for(k = 0; k < 10; k++)
			{
				t3 = rand() % 32767 + 1;
				bi_reset(a3);
				int2bytes(t3, a3);
				bi_powm(a1, a2, a3, a4);
				t4 = bytes2int(a4);
				//assert(t4 == powm(t1, t2, t3));
			}
		}
	}
	rsa_free(a1);
	rsa_free(a2);
	rsa_free(a3);
	rsa_free(a4);
	printf("Ok\n");
	fflush(stdout);
}

void test_special()
{
	u32_t a = 10837, p = 17255, n = 12183;
	u8_pt a1 = rsa_default_malloc();
	u8_pt p1 = rsa_default_malloc();
	u8_pt n1 = rsa_default_malloc();
	u8_pt r  = rsa_default_malloc();
	int2bytes(a, a1);
	int2bytes(p, p1);
	int2bytes(n, n1);

	bi_mod(a1, n1, r);
	printf("%d = %d mod %d, %d\n", a, a % n, n, bytes2int(r));
	i32_t bits = bi_bits(p1)-2;
	while(bits >= 0)
	{
		i32_t t = bi_get_bit(p1, bits);
		if (t == 0)
		{
			i32_t t = bytes2int(r);
			printf("now r 1: %d\n", t);
			bi_mul(r, r, r);
			printf("%d^2: %d\n", t, bytes2int(r));
			//assert(bytes2int(r) == t * t);
			bi_mod(r, n1, r);
			printf("%d^2 mod %d: %d\n", t, n, bytes2int(r));
			//assert(bytes2int(r) == ((t * t) % n));
		}
		else
		{
			i32_t t = bytes2int(r);
			printf("now r 2: %d\n", t);
			bi_mul(r, r, r);
			printf("%d^2: %d\n", t, bytes2int(r));
			//assert(bytes2int(r) == t * t);
			printf("now r's bits: %d\n", bi_bits(r));
			bi_mod(r, n1, r);
			printf("%d^2 mod %d: %d\n", t, n, bytes2int(r));
			//assert(bytes2int(r) == ((t * t) % n));
			bi_mul(r, a1, r);
			//assert(bytes2int(r) == ((t * t) % n) * a);
			bi_mod(r, n1, r);
			//assert(bytes2int(r) == (((a % n ) * ((t * t) % n)) % n));
		}
		bits --;
	}
	printf("%d^%d = %d mod %d, %d\n", a, p, powm(a, p, n), n, bytes2int(r));
	rsa_free(a1);
	rsa_free(p1);
	rsa_free(n1);
	rsa_free(r);
}

void test_special_2()
{
	u32_t a = 1351, b = 5770;
	u8_pt a1 = rsa_default_malloc();
	u8_pt b1 = rsa_default_malloc();
	int2bytes(a, a1);
	int2bytes(b, b1);
	bi_mul(a1, a1, a1);
	bi_mul(b1, b1, b1);
	printf("%d^2: %d\n", a, bytes2int(a1));
	printf("%d^2: %d\n", b, bytes2int(b1));
}

void test_bigint()
{
	test_add();
	test_sub();
	test_mul();
	test_div();
	test_mod();
	test_lshift();
	test_rshift();
	test_reset();
	test_bits();
	test_set_bit();
	test_get_bit();
	test_powm();
	test_gcd();
	test_comp();
}

